﻿#ifndef XQZIPPACK_H
#define XQZIPPACK_H
#include<QObject>
#include<QFileInfo>
#include<QDateTime>
#include"XQZipPack_zipInfo.h"
#ifdef _DEBUG
#pragma comment(lib,"x64-Debug/zlibwapi.lib")
#else
#pragma comment(lib,"x64-Release/zlibwapi.lib")
#endif // _DEBUG
const int BUFFER_SIZE = 4096;//文件缓冲区

class QRunnable;
typedef void* unzFile;
typedef void* zipFile;
class XQZipPack:public QObject
{
	Q_OBJECT
public:
	XQZipPack(QObject* parent = nullptr);
	~XQZipPack();
	//返回一个封装好的void()类型的函数指针，可以用用来多线程启动压缩文件或目录
	template<typename _zipFinish, typename _zipProgress>
	static std::function<void()> package(const QStringList& dirs, const QStringList& files, const QString& zipFileName,const QString password, _zipFinish zipFinishSlots , _zipProgress zipProgressSlots );
	//返回一个封装好的void()类型的函数指针，可以用用来多线程启动解压文件或目录
	template<typename _zipFinish, typename _zipProgress>
	static std::function<void()> unPackage(const QString& dirName, const QString& zipFileName, const QString password, _zipFinish zipFinishSlots, _zipProgress zipProgressSlots);
	//压缩数据(内存)，不到50字节压缩反而变大，超过100字节后有比较好的效果
	static QByteArray gzip(const QByteArray& data,int leve= -1);
	//解压数据(内存)
	static QByteArray unzip(const QByteArray& data);
	//解压deflate算法压缩的数据
	static QByteArray decompress(const QByteArray& data);
	void addDir(const QString& dir);
	void addDir(const QStringList& dir);
	void addFile(const QString& path);
	void addFile(const QStringList& path);
	//设置进度通知间隔
	void setProgressTime(int time);
public slots:
	//开始打包
	void package(const QString& packagePath, const QString& passWord = "", int level = 9);
	//开始解包
	void unPackage(const QString& PackageFilePath, const QString& UnPackageDir, const QString& password = "");
	/*************************************************
	Function:        unPackFileFromPackge
	Description:     解包压缩包中特定的一个文件
	Input:           strPackageFilePath，需要解包的文件路径
	Input:           strUnPackageDir，解压目录
	Input:           password，密码对应package打包的密码
	Input:           fileNameInPackage，该文件在包中的名称
	Output:          无
	Return:         void
	*************************************************/
	void unPackFileFromPackge(const QString& strPackageFilePath,const QString& strUnPackageDir,const QString& fileNameInPackage,const QString& password = "");
	//清空打包内容
	void clear();
	//获取文件列表
	QFileInfoList fileList(const QString& dirName);
	//统计QFileInfoList文件列表总大小
	size_t StatisticalDirFileSize(QFileInfoList& list);
	//统计目录下文件总大小并转字符串
	QString StatisticalDirFileSizeToString();
signals://信号
	//打包解包信息
	void progressInfo(const QString& info);
	//获取文件列表完成
	void fileListFinish(const QFileInfoList& list);
	////获取文件列表进度
	void fileListProgress(const QFileInfoList& List);
signals://信号
	//任务开始
	void zipStart(const XQZipPack_zipInfo& info);
	//压缩/解压文件完成
	void zipFinish(const XQZipPack_zipInfo& info);
	//压缩解压进度
	void zipProgress(const XQZipPack_zipInfo& info);
private:
	//发送进度信号
	void sendProgress();
	//获取文件列表中文件总大小
	size_t fileListSize(const QString& dirName, QFileInfoList* list);
	/*************************************************
   Function:        packageByDir
   Description:     对指定目录进行递归打包
   Input:           strPath，需要打包的目录
   Input:           parentDir，父目录
   Input:           password，密码
   Input:           level，压缩级别
   Output:          无
   Return:         void
   *************************************************/
	void packageByDir(zipFile* zf, const QString& strPath, const QString& parentDir, const QString& passWord, int level);
	/*************************************************
	Function:        packageByFile
	Description:     对指定文件行打包
	Input:           fileNameInPackage，指定打包的文件在包文件的名称
	Input:           filePath，需要打包文件的完整路径
	Input:           password，密码
	Input:           level，压缩级别
	Output:          无
	Return:         void
	*************************************************/
	void packageByFile(zipFile* zf, const QString& fileNameInPackage, const QString& filePath, const QString& passWord, int level);
	//解包获取文件信息
	QString unPackageGetCurrentFileInfo(unzFile* unzfile, const QString& UnPackageDir);

	XQZipPack_zipInfo m_zipInfo;//压缩解压时的信息
	QStringList m_files;//需要打包的文件列表
	QStringList m_dirs;//需要打包的目录列表
	int m_ProgressTime=500;//进度通知间隔
	QDateTime m_startTime;//记录的开始时间
};

#endif

template<typename _zipFinish, typename _zipProgress>
inline std::function<void()> XQZipPack::package(const QStringList& dirs, const QStringList& files, const QString& zipFileName, const QString password, _zipFinish zipFinishSlots, _zipProgress zipProgressSlots)
{
	auto func = [=] {
		XQZipPack zip;
		zip.addDir(dirs);
		zip.addFile(files);
		//判断是否绑定槽函数
		/*if (zipFinishSlots != NULL)*/
		connect(&zip, &XQZipPack::zipFinish, zipFinishSlots);
		/*if (zipProgressSlots != NULL)*/
		connect(&zip, &XQZipPack::zipProgress, zipProgressSlots);
		zip.package(zipFileName, password);
	};
	return func;
}

template<typename _zipFinish, typename _zipProgress>
inline std::function<void()> XQZipPack::unPackage(const QString& dirName, const QString& zipFileName, const QString password, _zipFinish zipFinishSlots, _zipProgress zipProgressSlots)
{
	auto func = [=] {
		XQZipPack zip;
		//判断是否绑定槽函数
		/*if (zipFinishSlots != NULL)*/
		connect(&zip, &XQZipPack::zipFinish, zipFinishSlots);
		/*if (zipProgressSlots != NULL)*/
		connect(&zip, &XQZipPack::zipProgress, zipProgressSlots);
		zip.unPackage(zipFileName,dirName,password );
	};
	return func;
}
